/*
 * Copyright (c) 2009 Robert Esser
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.e2ser.component;

import java.io.IOException;
import java.util.ArrayList;

import net.e2ser.PNBrowser;
import net.e2ser.petri.Color;
import net.e2ser.petri.Editor;
import net.e2ser.petri.RobPoint;
import net.e2ser.petri.SimulatorMode;
import net.e2ser.util.XMLWriter;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.util.Log;

/**
 * 
 * <p>
 * Title: Petri Net Editor/Simulator
 * </p>
 * <p>
 * Description: Draws and maintains transition information.
 * </p>
 * <p>
 * Copyright: Copyright (c) 2009
 * </p>
 * <p>
 * Company:
 * </p>
 * 
 * @author Rob Esser
 * @version 1.0
 */
public class Transition extends AbstractComponent {

  public Transition(RobComponent parent, double scale) {
    this(parent, 0, 0, scale);
  }

  public Transition(RobComponent parent, int x, int y, double scale) {
    super(parent, x, y);
    shape = new ShapeDrawable(new RectShape());
    Rect r = new Rect(getComponentBounds());
    r.right = r.left + (int) (AbstractComponent.DefaultComponentWidth() * scale);
    r.bottom = r.top + (int) (AbstractComponent.DefaultComponentHeight() * scale);
    shape.setBounds(r);
    fillColor = Color.GREEN;
    Paint paint = shape.getPaint();
    paint.setColor(fillColor);
    paint.setTextAlign(Align.CENTER);
    recalulateTextSize(); 
    paint.setTextSize(textSize());
    paint.setTypeface(Typeface.SANS_SERIF);
  }

  public Transition(RobComponent parent, String name, int x, int y, int width, int height, double scale) {
    super(parent, x, y, width, height);
    shape = new ShapeDrawable(new RectShape());
    // int left, int top, int right, int bottom
    shape.setBounds(x, y, x + width, y + height);
    resize(scale);
    this.name = name;
    fillColor = Color.GREEN;
    Paint paint = shape.getPaint();
    paint.setColor(fillColor);
    paint.setTextAlign(Align.CENTER);
    paint.setTextSize(textSize());
    paint.setTypeface(Typeface.SANS_SERIF);
  }

  protected boolean isFireable(SimulatorMode mode) {
    // am I fireable if so add myself to fireList
    ArrayList<AbstractComponent> allInputs = allInputs();
    int num = allInputs.size();
    for (int i = 0; i < num; i++) {
      if (((Place) allInputs.get(i)).tokensSize() == 0) {
        return false;
      }
    }
    if (mode.isCEMode()) {
      ArrayList<AbstractComponent> onlyOutputs = onlyOutputs();
      num = onlyOutputs.size();
      for (int i = 0; i < num; i++) {
        if (((Place) onlyOutputs.get(i)).tokensSize() > 0) {
          return false;
        }
      }
    }
    return true;
  }

  public void addFireable(ArrayList<AbstractComponent> fireList, SimulatorMode mode) {
    // am I fireable if so add myself to fireList
    if (this.isFireable(mode)) {
      fireList.add(this);
    }
  }

  public void fire(SimulatorMode mode, boolean animation, int delay) {
    // this is an error if it is called
    if (this.isFireable(mode)) {
      if (animation) {
        blink(delay);
      }
      ArrayList<AbstractComponent> allInputs = allInputs();
      int num = allInputs.size();
      for (int i = 0; i < num; i++) {
        ((Place) allInputs.get(i)).removeToken(animation);
      }
      ArrayList<AbstractComponent> allOutputs = allOutputs();
      num = allOutputs.size();
      for (int i = 0; i < num; i++) {
        ((Place) allOutputs.get(i)).addToken(animation);
      }
    } else {
      Log.v(PNBrowser.TAG, "Transition fire() called and not enabled");
    }
  }

  public AbstractComponent insertElementIn(RobComponent node, int x, int y, double scale) {
    AbstractComponent n = new Transition(node, x, y, scale);
    node.insertElementAt(n, x, y);
    return n;
  }

  /**
   * Calculate the intersection point of the arc and my perimeter
   */
  RobPoint connectionPoint(RobPoint otherEnd) {
    // where does my connection go to
    double newX, newY;
    RobPoint oldDelta = otherEnd.sub(this.center()).unitVector();
    RobPoint halfSize = new RobPoint(((double) shape.getBounds().width()) / 2.0, ((double) shape.getBounds().height()) / 2.0);
    oldDelta = oldDelta.mult(halfSize);
    if (Math.abs(oldDelta.x) > Math.abs(oldDelta.y)) {
      newX = halfSize.x;
      if (oldDelta.x < 0.0) {
        newX = 0.0 - newX;
      }
      if (oldDelta.x == 0.0) {
        newY = 0.0;
      } else {
        newY = (int) (Math.abs(halfSize.y * (oldDelta.y / oldDelta.x)));
      }
      if (oldDelta.y < 0.0) {
        newY = 0.0 - newY;
      }
    } else {
      newY = halfSize.y;
      if (oldDelta.y < 0.0) {
        newY = 0.0 - newY;
      }
      if (oldDelta.y == 0.0) {
        newX = 0.0;
      } else {
        newX = (int) (Math.abs(halfSize.x * (oldDelta.x / oldDelta.y)));
      }
      if (oldDelta.x < 0) {
        newX = 0.0 - newX;
      }
    }
    return new RobPoint(newX, newY).add(this.center());
  }

  /**
   * draw myself on the Canvas
   * 
   * @param canvas
   *          where to draw
   */
  public void draw(Canvas canvas) {
    if (inverse) {
      shape.getPaint().setColor(Color.GRAY);
    } else {
      shape.getPaint().setColor(fillColor);
    }
    // draw body
    shape.draw(canvas);
    // draw border
    shape.getPaint().setColor(Color.BLACK);
    Rect r = shape.getBounds();
    canvas.drawLine(r.left, r.top, r.right, r.top, shape.getPaint());
    canvas.drawLine(r.left, r.bottom - 1, r.right, r.bottom - 1, shape.getPaint());
    canvas.drawLine(r.left, r.top, r.left, r.bottom, shape.getPaint());
    canvas.drawLine(r.right - 1, r.top, r.right - 1, r.bottom, shape.getPaint());

     // now the inscription text
     String s = (name == null) ? "trans": name;
     if (s.length() > 0) {
       shape.getPaint().setColor(Color.GRAY);
       canvas.drawText(s, r.centerX() + Editor.TextOffsetX, r.centerY() + Editor.TextOffsetY, shape.getPaint());
     }
  }

  /**
   * Draw a marker to indicate that the node is selected
   * @param canvas where to draw
   */
  public void drawSelected(Canvas canvas) {
    shape.getPaint().setColor(Color.darker(Color.darker(fillColor)));
    shape.getPaint().setStrokeWidth(3);
    Rect r = new Rect(shape.getBounds());
    r.inset(2, 2);
    canvas.drawLine(r.left, r.top, r.right, r.top, shape.getPaint());
    canvas.drawLine(r.left, r.bottom - 1, r.right, r.bottom - 1, shape.getPaint());
    canvas.drawLine(r.left, r.top, r.left, r.bottom, shape.getPaint());
    canvas.drawLine(r.right - 1, r.top, r.right - 1, r.bottom, shape.getPaint());
    shape.getPaint().setStrokeWidth(1);
  }

  /**
   * Output an XML representation of myself
   * @param writer object that represents writer state
   * @throws IOException 
   */
  public void toXML(XMLWriter writer) throws IOException {
    String id = writer.addElement(this);
    Rect r = shape.getBounds();
    writer.writeLine("<trans id=\"" + id + "\" l=\"" + r.left + 
        "\" t=\"" + r.top + "\" r=\"" + r.right + 
        "\" b=\"" + r.bottom + 
        (name == null ? "" : "\" n=\"" + name) + "\"/>");
  }
  
}
